package chapter03_binaryTree;

/**
 * 描述：
 *      找到二叉树中结点最多的搜索二叉子树，并返回子树的根结点
 * @author hl
 * @date 2021/5/9 10:29
 */
public class BiggestSubBST {

    public static void main(String[] args) {
        Node head = new Node(6);
        head.left = new Node(1);
        head.left.left = new Node(0);
        head.left.right = new Node(3);
        head.right = new Node(12);
        head.right.left = new Node(10);
        head.right.left.left = new Node(4);
        head.right.left.left.left = new Node(2);
        head.right.left.left.right = new Node(5);
        head.right.left.right = new Node(14);
        head.right.left.right.left = new Node(11);
        head.right.left.right.right = new Node(15);
        head.right.right = new Node(13);
        head.right.right.left = new Node(20);
        head.right.right.right = new Node(16);
        Node node = new BiggestSubBST().biggestSubBST(head);
        System.out.println(node);
    }

    /**
     * 可以用数组作为参数传递帮助收集遍历过程中的信息，而取代定义新的返回结构
     * @param head
     * @return
     */
    public Node biggestSubBST(Node head){
        //record收集结点的数量、子树的最小值以及子树的最大值
        int[] record = new int[3];
        return posOrder(head, record);
    }

    private Node posOrder(Node node, int[] record) {
        if (node == null) {
            record[0] = 0;
            record[1] = Integer.MAX_VALUE;
            record[2] = Integer.MIN_VALUE;
            return null;
        }
        Node left = node.left;
        Node right = node.right;
        int value = node.val;
        Node lBST = posOrder(left, record);
        int lSize = record[0];
        int lMin = record[1];
        int lMax = record[2];
        Node rBST = posOrder(right, record);
        int rSize = record[0];
        int rMin = record[1];
        int rMax = record[2];
        record[1] = Math.min(lMin, value);
        record[2] = Math.max(rMax, value);
        if (left == lBST && right == rBST && value >= lMax && value <= rMin) {
            record[0] = lSize + rSize + 1;
            return node;
        }
        record[0] = Math.max(lSize, rSize);
        return lSize > rSize ? lBST : rBST;
    }

    public Node biggestSubBST2(Node head){
        if (head == null) {
            return null;
        }
        return searchBiggestSubBST(head).mostBSTHead;
    }

    private ReturnType searchBiggestSubBST(Node head) {
        if (head == null) {
            return new ReturnType(true);
        }
        ReturnType returnType = new ReturnType(true, 0, null, null);
        ReturnType leftReturn = searchBiggestSubBST(head.left);
        ReturnType rightReturn = searchBiggestSubBST(head.right);
        if (!leftReturn.isBST || !rightReturn.isBST
                || (leftReturn.rightMostNode != null && leftReturn.rightMostNode.val > head.val)
                || (rightReturn.leftMostNode != null && rightReturn.leftMostNode.val < head.val)) {
            returnType.isBST = false;
            if (leftReturn.mostNodeNum > rightReturn.mostNodeNum) {
                returnType.mostNodeNum = leftReturn.mostNodeNum;
                returnType.mostBSTHead = leftReturn.mostBSTHead;
            }else{
                returnType.mostNodeNum = rightReturn.mostNodeNum;
                returnType.mostBSTHead = rightReturn.mostBSTHead;
            }
        }else{
            returnType.isBST = true;
            returnType.mostBSTHead = head;
            int curNodeNum = leftReturn.nodeNum + rightReturn.nodeNum + 1;
            returnType.nodeNum = curNodeNum;
            returnType.mostNodeNum = curNodeNum;
            returnType.leftMostNode = leftReturn.leftMostNode == null ? head : leftReturn.leftMostNode;
            returnType.rightMostNode = rightReturn.rightMostNode == null ? head : rightReturn.rightMostNode;
        }
        return returnType;
    }

}
class ReturnType{
    boolean isBST;
    int nodeNum;
    int mostNodeNum;
    Node mostBSTHead;
    Node leftMostNode;
    Node rightMostNode;


    public ReturnType(boolean isBST) {
        this.isBST = isBST;
    }

    public ReturnType(boolean isBST, int nodeNum, Node leftMostNode, Node rightMostNode) {
        this.isBST = isBST;
        this.nodeNum = nodeNum;
        this.leftMostNode = leftMostNode;
        this.rightMostNode = rightMostNode;
    }
}